package com.cspm.common.util;

import org.apache.log4j.Logger;

/**
 * 生成唯一编号
 *
 * @author CHQIU
 */
public class IdWorker {

    protected static final Logger LOG = Logger.getLogger(IdWorker.class);

    private final long workerId;
    private final long datacenterId;
    private long sequence = 0L;

    private final long twepoch = 1288834974657L;
    // 机器标识位数
    private final long workerIdBits = 3L;
    // 数据中心标识位数
    private final long datacenterIdBits = 5L;
    // 机器ID最大值
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    // 数据中心ID最大值
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    // 毫秒内自增位
    private final long sequenceBits = 2L;
    // 机器ID偏左移12位
    private final long workerIdShift = sequenceBits;
    // 数据中心ID左移17位
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    // 时间毫秒左移22位
    private final long timestampLeftShift = sequenceBits + workerIdBits
            + datacenterIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long lastTimestamp = -1L;

    public IdWorker() {
        this(0L, 0L);
    }

    public IdWorker(long workerId, long datacenterId) {
        // sanity check for workerId
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("机器Id不能大于%d小于0",
                    maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("数据中心Id不能大于%d小于0",
                    maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    /*
	 * System.out.println(String.format(
	 * "开始生成：时间毫秒左移% d,数据中心Id % d,机器Id% d毫秒内自增位% d,唯一编号 % d",
	 * timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits,
	 * workerId));
	 */
    }

    public synchronized long nextId() {
        long timestamp = timeGen();
        // 时间错误
        if (timestamp < lastTimestamp) {
            LOG.error(String.format("时钟是向后移动。拒绝请求,直到% d。", lastTimestamp));
            throw new RuntimeException(String.format("时钟倒退。拒绝为% d生成id毫秒",
                    lastTimestamp - timestamp));
        }

        if (lastTimestamp == timestamp) {
            // 当前毫秒内，则+1
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 当前毫秒内计数满了，则等待下一秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;
        // ID偏移组合生成最终的ID，并返回ID
        return ((timestamp - twepoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift) | sequence;
    }

    // 等待下一个毫秒的到来
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    protected long timeGen() {
        return System.currentTimeMillis();
    }
}